Panic
panic! is a macro that immediately stops normal program execution when something goes seriously wrong. It’s used for unrecoverable errors—situations where the program cannot safely continue.
panic!("Something went terribly wrong!");
When this runs, Rust:
- Prints an error message.
- Shows a stack trace (if enabled).
- Terminates the program (or unwinds the stack).
When should you use panic!?
Use panic! when:
- A bug has occurred.
- An assumption in your code is violated.
- Continuing would cause incorrect behavior or data corruption.
Do not use panic! for expected errors like invalid user input or file-not-found—those should use Result.
How Rust handles a panic
Rust can handle panics in two ways (controlled by build settings):
- Unwinding (default)
- Rust walks back through the stack and runs destructors.
- Aborting
- Program stops immediately (faster, no cleanup).
Example 1: Simple panic!
fn main() {
panic!("Crash and burn!");
}
Output (simplified):
thread 'main' panicked at 'Crash and burn!', src/main.rs:2:5
The program stops immediately.
Example 2: Panic from an invalid operation
fn main() {
let v = vec![1, 2, 3];
println!("{}", v[99]); // Out of bounds → panic!
}
Why this panics:
- Rust checks array bounds at runtime.
- Accessing index
99is invalid → Rust callspanic!internally.
Example 3: Using unwrap() (which may panic)
fn main() {
let number: Option<i32> = None;
let value = number.unwrap(); // panic!
println!("{}", value);
}
unwrap()extracts the value if it exists.- If the value is
None, it callspanic!. - This is convenient for quick prototypes but risky in production code.
Example 4: Custom panic with context
fn divide(a: i32, b: i32) -> i32 {
if b == 0 {
panic!("Cannot divide by zero!");
}
a / b
}
fn main() {
let result = divide(10, 0);
println!("{}", result);
}
- Division by zero is an unrecoverable logic error here.
- We explicitly panic with a helpful message.
panic! vs Result
Use panic! when... | Use Result when... |
|---|---|
| Bug in the code | User input error |
| Impossible state | File not found |
| Program cannot continue safely | Network failure |
Example using Result instead of panic!:
fn divide(a: i32, b: i32) -> Result<i32, String> {
if b == 0 {
Err("Cannot divide by zero".to_string())
} else {
Ok(a / b)
}
}
fn main() {
match divide(10, 0) {
Ok(result) => println!("Result: {}", result),
Err(e) => println!("Error: {}", e),
}
}
This allows the program to continue safely.
Catching a panic (advanced)
You can catch panics using std::panic::catch_unwind, but this is usually for:
- Testing
- FFI boundaries
- Highly robust systems
use std::panic;
fn main() {
let result = panic::catch_unwind(|| {
panic!("This will be caught!");
});
match result {
Ok(_) => println!("No panic."),
Err(_) => println!("Panic was caught!"),
}
}
Summary
panic!is for unrecoverable errors.- It stops execution immediately.
- Common sources:
unwrap(), array out-of-bounds, explicitpanic!. - Prefer
Resultfor recoverable errors. - Use
panic!to signal bugs, not user mistakes.